import { defineStore } from 'pinia';
import YAML from 'yaml';

export interface PostMeta {
  slug: string;
  title: string;
  date?: string;
  tags?: string[];
  cover?: string;
  excerpt?: string;
  readingMinutes?: number;
}

export interface PostDetail extends PostMeta {
  html: string;
}

function parseTitleFromMarkdown(markdown: string): string {
  const lines = markdown.split(/\r?\n/);
  for (const line of lines) {
    const m = /^#\s+(.+)/.exec(line.trim());
    if (m) return m[1].trim();
  }
  // 兜底：取首行的前 60 字符
  return lines.find(l => l.trim().length > 0)?.trim().slice(0, 60) ?? '未命名文章';
}

function extractFrontmatter(markdown: string): { data: Record<string, unknown>, content: string } {
  // 允许以 --- 开头的 YAML frontmatter
  if (markdown.startsWith('---')) {
    const end = markdown.indexOf('\n---', 3);
    if (end !== -1) {
      const fmBlock = markdown.slice(3, end).replace(/^\n/, '');
      const content = markdown.slice(end + 4).replace(/^\s*\n/, '');
      try {
        const data = YAML.parse(fmBlock) || {};
        return { data, content };
      } catch {
        // 解析失败时忽略 frontmatter
        return { data: {}, content: markdown };
      }
    }
  }
  return { data: {}, content: markdown };
}

function extractFirstImage(markdownContent: string): string | undefined {
  // 1) Markdown 图片: ![alt](url "title")
  const mdImg = /!\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/.exec(markdownContent);
  if (mdImg && mdImg[1]) return mdImg[1];
  // 2) HTML 图片: <img src="...">
  const htmlImg = /<img[^>]+src=["']([^"']+)["'][^>]*>/i.exec(markdownContent);
  if (htmlImg && htmlImg[1]) return htmlImg[1];
  return undefined;
}

function stripMarkdown(markdownContent: string): string {
  // remove code fences
  let text = markdownContent.replace(/```[\s\S]*?```/g, '');
  // remove html tags
  text = text.replace(/<[^>]+>/g, '');
  // remove images/links syntax
  text = text.replace(/!\[[^\]]*\]\([^)]*\)/g, '');
  text = text.replace(/\[[^\]]*\]\([^)]*\)/g, '');
  // remove headings and markdown tokens
  text = text.replace(/^#{1,6}\s+/gm, '');
  text = text.replace(/[*_`>#-]+/g, ' ');
  // collapse whitespace
  return text.replace(/\s+/g, ' ').trim();
}

function estimateReadingMinutes(plainText: string): number {
  const minutes = Math.ceil(plainText.length / 500); // approx 500 汉字/分
  return Math.max(1, minutes);
}

function hashStringToInt(input: string): number {
  let hash = 0;
  for (let i = 0; i < input.length; i++) {
    hash = (hash << 5) - hash + input.charCodeAt(i);
    hash |= 0;
  }
  return Math.abs(hash);
}

function pickGradientColors(seed: string): [string, string] {
  const palette: Array<[string, string]> = [
    ['#1e3a8a', '#0ea5e9'], // blue
    ['#134e4a', '#10b981'], // green
    ['#3f1d4a', '#a855f7'], // purple
    ['#7c2d12', '#f59e0b'], // orange
    ['#0f172a', '#38bdf8'], // slate-cyan
    ['#1d2538', '#00ff9c']  // night-neon
  ];
  const idx = hashStringToInt(seed) % palette.length;
  return palette[idx];
}

function generateTitleCoverDataUrl(title: string, seed: string): string | undefined {
  if (typeof document === 'undefined') return undefined;
  const width = 1200, height = 630;
  const canvas = document.createElement('canvas');
  canvas.width = width; canvas.height = height;
  const ctx = canvas.getContext('2d');
  if (!ctx) return undefined;
  const [c1, c2] = pickGradientColors(seed);
  const gradient = ctx.createLinearGradient(0, 0, width, height);
  gradient.addColorStop(0, c1);
  gradient.addColorStop(1, c2);
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, width, height);

  // overlay grid
  ctx.globalAlpha = 0.08;
  ctx.strokeStyle = '#ffffff';
  for (let x = 0; x <= width; x += 40) {
    ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke();
  }
  for (let y = 0; y <= height; y += 40) {
    ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke();
  }
  ctx.globalAlpha = 1;

  // draw title with wrapping
  ctx.fillStyle = '#e6f1ff';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  const maxWidth = width * 0.8;
  let fontSize = 64;
  const fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Fira Code", "JetBrains Mono", monospace';
  function setFont() { ctx.font = `700 ${fontSize}px ${fontFamily}`; }
  setFont();

  function wrapText(text: string): string[] {
    const words = text.split(/\s+/);
    const lines: string[] = [];
    let line = '';
    for (const w of words) {
      const test = line ? line + ' ' + w : w;
      if (ctx.measureText(test).width > maxWidth) {
        if (line) lines.push(line);
        line = w;
      } else {
        line = test;
      }
    }
    if (line) lines.push(line);
    return lines;
  }

  let lines = wrapText(title);
  while (lines.length > 3 && fontSize > 28) {
    fontSize -= 4; setFont(); lines = wrapText(title);
  }

  const lineHeight = fontSize * 1.2;
  const totalHeight = lineHeight * lines.length;
  let y = height / 2 - totalHeight / 2;
  for (const l of lines) {
    ctx.fillText(l, width / 2, y + lineHeight / 2);
    y += lineHeight;
  }

  // subtle border
  ctx.strokeStyle = 'rgba(230,241,255,0.2)';
  ctx.lineWidth = 8;
  ctx.strokeRect(4, 4, width - 8, height - 8);

  return canvas.toDataURL('image/png');
}

export const usePostsStore = defineStore('posts', {
  state: () => ({
    metas: [] as PostMeta[],
    cache: new Map<string, PostDetail>(),
    allTags: [] as string[]
  }),
  actions: {
    resolveCover(input: unknown): string | undefined {
      if (typeof input !== 'string') return undefined;
      const value = input.trim();
      if (!value) return undefined;
      if (value.startsWith('http://') || value.startsWith('https://') || value.startsWith('/')) return value;
      return '/' + value;
    },
    normalizeTags(input: unknown): string[] | undefined {
      if (!input) return undefined;
      if (Array.isArray(input)) {
        const result = input
          .map(v => String(v).trim())
          .filter(Boolean);
        return result.length ? result : undefined;
      }
      if (typeof input === 'string') {
        const result = input
          .split(/[,\s]+/)
          .map(s => s.trim())
          .filter(Boolean);
        return result.length ? result : undefined;
      }
      return undefined;
    },

    async loadMetas() {
      if (this.metas.length) return;
      const modules = import.meta.glob<string>(
        '/articles/**/*.md',
        { query: '?raw', import: 'default' }
      );
      const metas: PostMeta[] = [];
      for (const path in modules) {
        const loader = modules[path];
        const markdown = await loader();
        const { data, content } = extractFrontmatter(markdown);
        const title = (typeof data.title === 'string' && data.title.trim()) || parseTitleFromMarkdown(content);
        const filename = path.split('/').pop() || path;
        const slug = filename.replace(/\.md$/, '');
        const tags = this.normalizeTags((data as any).tags);
        const date = typeof (data as any).date === 'string' ? (data as any).date : undefined;
        let cover = this.resolveCover((data as any).cover);
        if (!cover) {
          const first = extractFirstImage(content);
          cover = this.resolveCover(first);
        }
        if (!cover) {
          cover = generateTitleCoverDataUrl(title, slug) || undefined;
        }
        const plain = stripMarkdown(content);
        const excerpt = plain.slice(0, 120);
        const readingMinutes = estimateReadingMinutes(plain);
        metas.push({ slug, title, tags, date, cover, excerpt, readingMinutes });
      }
      // 若存在日期，按日期倒序，否则按标题
      metas.sort((a, b) => {
        if (a.date && b.date) return b.date.localeCompare(a.date);
        if (a.date) return -1;
        if (b.date) return 1;
        return a.title.localeCompare(b.title, 'zh-CN');
      });
      this.metas = metas;
      const tagSet = new Set<string>();
      for (const m of metas) {
        m.tags?.forEach(t => tagSet.add(t));
      }
      this.allTags = Array.from(tagSet).sort((a, b) => a.localeCompare(b, 'zh-CN'));
    },

    async getPost(slug: string): Promise<PostDetail> {
      const cached = this.cache.get(slug);
      if (cached) return cached;
      const modules = import.meta.glob<string>(
        '/articles/**/*.md',
        { query: '?raw', import: 'default' }
      );
      // 尝试匹配 slug
      const matchEntry = Object.entries(modules).find(([path]) =>
        path.endsWith(`/${slug}.md`)
      );
      if (!matchEntry) throw new Error('未找到文章: ' + slug);
      const markdown = await matchEntry[1]();
      const { data, content } = extractFrontmatter(markdown);
      const title = (typeof data.title === 'string' && data.title.trim()) || parseTitleFromMarkdown(content);
      const { default: MarkdownIt } = await import('markdown-it');
      const md = new MarkdownIt({ html: true, linkify: true, typographer: true });
      const html = md.render(content);
      const tags = this.normalizeTags((data as any).tags);
      const date = typeof (data as any).date === 'string' ? (data as any).date : undefined;
      let cover = this.resolveCover((data as any).cover);
      if (!cover) {
        const first = extractFirstImage(content);
        cover = this.resolveCover(first);
      }
      if (!cover) {
        cover = generateTitleCoverDataUrl(title, slug) || undefined;
      }
      const detail: PostDetail = { slug, title, html, tags, date, cover };
      this.cache.set(slug, detail);
      return detail;
    }
  }
});

